﻿using System;
using System.Collections.Generic;
using System.Text;

namespace PlutoStudio
{
    /// <summary>
    /// Extension methods for Dictionary.
    /// </summary>
    public static class DictionaryExtensions
    {
        /// <summary>
        /// This method is used to try to get a value in a dictionary if it does exists.
        /// </summary>
        /// <typeparam name="T">Type of the value</typeparam>
        /// <param name="dictionary">The collection object</param>
        /// <param name="key">Key</param>
        /// <param name="value">Value of the key (or default value if key not exists)</param>
        /// <returns>True if key does exists in the dictionary</returns>
        public static bool TryGetValue<T>(this IDictionary<string, object> dictionary, string key, out T value)
        {
            object valueObj;
            if (dictionary.TryGetValue(key, out valueObj) && valueObj is T)
            {
                value = (T)valueObj;
                return true;
            }

            value = default(T);
            return false;
        }

        /// <summary>
        /// 从给定的键获取 <see cref="IDictionary{TKey, TValue}"/>中的值。 如果找不到，返回默认值。
        /// </summary>
        /// <param name="dictionary">需要检查并获取的 <see cref="IDictionary{TKey, TValue}"/></param>
        /// <param name="key">要查找的键</param>
        /// <typeparam name="TKey">键类型</typeparam>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <returns>如果找到，则返回值，否则返回默认值。</returns>
        public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
        {
            TValue obj;
            return dictionary.TryGetValue(key, out obj) ? obj : default(TValue);
        }

        /// <summary>
        /// 从给定的键获取 <see cref="IDictionary{TKey, TValue}"/>中的值。 如果找不到，返回默认值。
        /// </summary>
        /// <param name="dictionary">需要检查并获取的 <see cref="IDictionary{TKey, TValue}"/></param>
        /// <param name="key">要查找的键</param>
        /// <param name="factory">在没有找到值时用于创建值的工厂方法。</param>
        /// <typeparam name="TKey">键类型</typeparam>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <returns>如果找到，则返回值，否则返回默认值。</returns>
        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> factory)
        {
            TValue obj;
            if (dictionary.TryGetValue(key, out obj))
            {
                return obj;
            }

            return dictionary[key] = factory(key);
        }

        /// <summary>
        /// 从给定的键获取 <see cref="IDictionary{TKey, TValue}"/>中的值。 如果找不到，返回默认值。
        /// </summary>
        /// <param name="dictionary">需要检查并获取的 <see cref="IDictionary{TKey, TValue}"/></param>
        /// <param name="key">要查找的键</param>
        /// <param name="factory">在没有找到值时用于创建值的工厂方法。</param>
        /// <typeparam name="TKey">键类型</typeparam>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <returns>如果找到，则返回值，否则返回默认值。</returns>
        public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> factory)
        {
            return dictionary.GetOrAdd(key, k => factory());
        }

        /// <summary>
        /// 对 <see cref="IDictionary{TKey, TValue}"/> 进行排序。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <typeparam name="TKey">键类型</typeparam>
        /// <param name="dict">需要排序的集合</param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> Sort<TKey,TValue>(this IDictionary<TKey, TValue> dict)
        {
            return new SortedDictionary<TKey, TValue>(dict);
        }

        #region IDictionary的扩展

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static TObject ToObject<TObject>(this IDictionary<string, string> dictionary)
        {
            return (TObject)dictionary.ToObject(typeof(TObject));
        }

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <param name="type">转化的目标对象类型</param>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static object ToObject(this IDictionary<string, string> dictionary, Type type)
        {
            var result = Activator.CreateInstance(type);
            var properties = System.ComponentModel.TypeDescriptor.GetProperties(type);
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = properties.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value.Deserialize(property.PropertyType);
                property.SetValue(result, value);
            }
            return result;
        }


        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static TObject ToObject<TObject>(this IDictionary<string, object> dictionary)
        {
            var result = Activator.CreateInstance<TObject>();
            var type = System.ComponentModel.TypeDescriptor.GetProperties(typeof(TObject));
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = type.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value;
                property.SetValue(result, Convert.ChangeType(value, property.PropertyType));
            }
            return result;
        }

        /// <summary>
        /// 将IDictonary的键值对转化成指定类型的对象。无法匹配的值将被忽略。
        /// </summary>
        /// <typeparam name="TObject">转化的目标对象类型</typeparam>
        /// <param name="dictionary">要转化的键值对</param>
        /// <returns>转化后的对象</returns>
        public static object ToObject(this IDictionary<string, object> dictionary, Type type)
        {
            var result = Activator.CreateInstance(type);
            var properties = System.ComponentModel.TypeDescriptor.GetProperties(type);
            foreach (var item in dictionary)
            {
                var propertyName = item.Key;
                var property = properties.Find(propertyName, true);
                if (property == null)
                {
                    continue;
                }
                if (item.Value == null)
                {
                    continue;
                }
                var value = item.Value;
                property.SetValue(result, Convert.ChangeType(value, property.PropertyType));
            }
            return result;
        }

        /// <summary>
        /// 将指定的对象转换成键值对。
        /// </summary>
        /// <param name="value">要转换的对象</param>
        /// <returns>生成的键值对</returns>
        public static IDictionary<string, object> ToDictionary(this object value)
        {
            return new RouteValueDictionary(value);
        }

        /// <summary>
        /// 将指定的对象转换成键值对。
        /// </summary>
        /// <param name="value">要转换的对象</param>
        /// <param name="useDescription">是否使用<see cref="DescriptionAttribute"/>作为<see cref="IDictionary{TKey, TValue}"/>的键。</param>
        /// <returns>生成的键值对</returns>
        public static IDictionary<string, object> ToDictionary(this object value, bool useDescription)
        {
            return new RouteValueDictionary(value, useDescription);
        }



        #endregion


    }
}
